package cn.virens.common;

import java.io.Serializable;
import java.nio.ByteBuffer;

import cn.virens.common.utils.number.ByteUtil;

public class RingByteBuffer implements Serializable {
	private static final long serialVersionUID = 3372057734674057722L;

	private final byte[] bytes;
	private final int capacity;

	private int index;
	private int length;
	private int marker;

	public RingByteBuffer(int capacity) {
		this.bytes = new byte[capacity];
		this.capacity = capacity;
	}

	public byte read() {
		if (length == 0 || length < 1) return -1;
		byte answer = this.bytes[index];

		// 将当前下标偏移1个字节
		this.length = len(length - 1);
		this.index = idx(index + 1);

		// 返回当前字节
		return answer;
	}

	public byte read(int offset) {
		if (0 >= offset || offset > length) return -1;
		if (length == 0 || length > capacity) return -1;

		// 返回指定字节
		return bytes[idx(index + offset)];
	}

	public short readShort() {
		if (length == 0 || length < 2) return -1;

		byte byte1 = this.bytes[idx(index + 0)];
		byte byte2 = this.bytes[idx(index + 1)];

		// 将当前下标偏移2个字节
		this.length = len(length - 2);
		this.index = idx(index + 2);

		// 返回当前字节
		return (short) (byte1 << 8 | byte2);
	}

	public short readShort(int offset) {
		if (0 >= offset || offset > length) return -1;
		if (length == 0 || length > capacity) return -1;

		byte byte1 = this.bytes[idx(index + offset + 0)];
		byte byte2 = this.bytes[idx(index + offset + 1)];

		// 返回当前字节
		return (short) (byte1 << 8 | byte2);
	}

	public int index() {
		return index;
	}

	public int index(int offset) {
		if (0 >= offset || offset > length) return -1;
		if (length == 0 || length > capacity) return -1;

		// 将当前下标偏移指定数目
		this.length = len(length - offset);
		this.index = idx(index + offset);

		// 返回下标
		return index;
	}

	public int length() {
		return length;
	}

	public int reset(int offset) {
		// 计算marker点与当前下标偏移数值
		int readLen = index - marker;
		if (readLen < 0) {
			readLen = readLen + capacity;
		}

		// 计算原始数据长度
		int origLen = length + readLen;

		// 恢复标注点的下标及数据长度
		this.length = len(origLen - offset);
		this.index = idx(marker + offset);

		// 返回当前下标
		return index;
	}

	public int marker() {
		if (length == 0) return -1;

		// 将当前下标记入标记点
		this.marker = index;

		// 返回标记点
		return marker;
	}

	public byte[] array() {
		byte[] answer = new byte[length];

		int indexEnd = index;
		for (int i = 0; i < length; i++) {
			answer[i] = this.bytes[idx(indexEnd++)];
		}

		return answer;
	}

	public void write(byte data) {
		this.bytes[idx((index + length))] = data;

		// 判断增加一个字节后数据长度是否溢出
		if (this.length + 1 > capacity) {
			// 长度溢出，起点加1，长度不变
			this.index = (index + 1) % capacity;
			this.length = capacity;
		} else {
			this.length = length + 1;
		}
	}

	public void write(byte[] data) {
		int indexStart = Math.max(data.length - capacity, 0);
		int indexEnd = (index + length) % capacity;

		// 只要传入数据中从后开始长度为size部分
		for (int i = indexStart; i < data.length; i++) {
			this.bytes[idx(indexEnd++)] = data[i];
		}

		// 计算数据长度
		this.length = Math.min(length + data.length, capacity);

		// 根据最后一个字节下标减掉数据长度计算下标起点
		int indexVal = indexEnd - length;
		if (indexVal < 0) {
			indexVal = indexVal + capacity;
		}

		// 赋值下标位置（防止溢出）
		this.index = indexVal % capacity;
	}

	public void write(ByteBuffer data) {
		int remaining = data.remaining();
		int indexEnd = (index + length) % capacity;
		int indexStart = Math.max(remaining - capacity, 0);

		// 只要传入数据中从后开始长度为size部分
		for (int i = indexStart; i < remaining; i++) {
			this.bytes[idx(indexEnd++)] = data.get();
		}

		// 计算数据长度
		this.length = Math.min(length + remaining, capacity);

		// 根据最后一个字节下标减掉数据长度计算下标起点
		int indexVal = indexEnd - length;
		if (indexVal < 0) {
			indexVal = indexVal + capacity;
		}

		// 赋值下标位置（防止溢出）
		this.index = indexVal % capacity;
	}

	public int getIndex() {
		return index;
	}

	public void setIndex(int index) {
		this.index = index;
	}

	public int getLength() {
		return length;
	}

	public void setLength(int length) {
		this.length = length;
	}

	public int getMarker() {
		return marker;
	}

	public void setMarker(int marker) {
		this.marker = marker;
	}

	@Override
	public String toString() {
		return ByteUtil.toHex(array());
	}

	private int idx(int index) {
		return index % capacity;
	}

	private int len(int length) {
		return Math.min(length, capacity);
	}
}
